/******************************************************************************
 *  author: enquanyan
 *  version: 1.0
 *  description: http_conn implementation
 *  date: 2014-07-21
 *  other: 无
 ******************************************************************************/

#include "http_conn.h"

const char* ok_200_title = "OK";
const char* ok_200_string = "<html><body>file is uploaded sucessly!</body></html>\r\n";
const char* ok_200_file_exist_string = "<html><body>File already exists!</body></html>\r\n";
		
const char* error_400_title = "Bad Request";
//const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n";
const char* error_400_form = "success=0";
		
const char* error_403_title = "Forbidden";
const char* error_403_form =
		"You do not have permission to #FFFFFFget file from this server.\n";
		
const char* error_404_title = "Not Found";
//const char* error_404_form = "The requested file was not found on this server.\n";
const char* error_404_form = "success=0";
		
const char* error_500_title = "Internal Error";
const char* error_500_form =
		"There was an unusual problem serving the requested file.\n";
		
const char* list_dir_start =
		"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/></head><body>";
const char* list_dir_end = "</body></html>";

int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

int addfd(int epollfd, int fd, bool one_shot)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
	if (one_shot)
	{
		event.events |= EPOLLONESHOT;
	}

	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) < 0)
	{
		return -1;
	}

	return setnonblocking(fd);
}

int addfd(int epollfd, abstract_conn *abstract_conn_ptr, int fd, bool one_shot)
{
	epoll_event event;
    abstract_conn_ptr->m_sockfd = fd;
	event.data.ptr = abstract_conn_ptr;
	event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
	if (one_shot)
	{
		event.events |= EPOLLONESHOT;
	}

	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) < 0)
	{
        pr_debug("epoll add fd error->sockfd:%d->errno:%d\n", fd, errno);
		return -1;
	}

	return setnonblocking(fd);
}

int removefd(int epollfd, int fd, bool is_in_epoll)
{
	if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0) < 0)
	{
        pr_debug("epoll remove fd error->sockfd:%d->errno:%d\n", fd, errno);
	}
		
	close(fd);
	return 0;
}

int modfd(int epollfd, int fd, int ev, bool is_in_epoll)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
	int ret;
	
	ret = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
	
	if(ret < 0)
	{
        pr_debug("epoll mod fd error->sockfd:%d->errno:%d\n", fd, errno);
	}
	
	return 0;
}

int modfd(int epollfd, abstract_conn *abstract_conn_ptr, int fd, int ev, bool is_in_epoll)
{
	epoll_event event;
    abstract_conn_ptr->m_sockfd = fd;
	event.data.ptr = abstract_conn_ptr;
	event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
	int ret;
	
	ret = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
	
	if(ret < 0)
	{
        pr_debug("epoll mod fd error->sockfd:%d->errno:%d\n", fd, errno);
	}
	
	return 0;
}

int http_conn::m_user_count = 0;
int http_conn::m_epollfd = -1;

http_conn::http_conn()
{
    this->conn_type = HTTP_CONN_T;
}
        
http_conn::~http_conn()
{
            
}

void http_conn::close_conn(bool real_close) 
{
	if (real_close && (m_sockfd != -1))
	{
		
		//pr_debug("close_conn->sockfd:%d->thread_no:%d\n", m_sockfd, thread_no);
		
		removefd(m_epollfd, m_sockfd, is_in_epoll);
		
		thread_arg *thread_arg_pointer = threadpool<abstract_conn>::thread_arg_array + thread_no;
		
		thread_arg_pointer->active_http_conn--;
		
		int active_http_conn = thread_arg_pointer->active_http_conn.load(std::memory_order_seq_cst);
		
		pr_debug("close_conn->sockfd:%d->thread_no:%d->--active_http_conn:%d\n", m_sockfd, thread_no, active_http_conn);
		
		if(thread_arg_pointer->is_first)
		{
			thread_arg_pointer->interval_handled_http_conn_first++;
		}
		else
		{
			thread_arg_pointer->interval_handled_http_conn_second++;
		}
        
        if(download_file_id > 0)
        {
            close(download_file_id);
        }
        
        if(upload_file_id > 0)
        {
            close(upload_file_id);
        }
        
        
	}
}

void http_conn::init(int sockfd, const sockaddr_in& addr) 
{
	m_sockfd = sockfd;
	m_address = addr;
	
	int error = 0;
	socklen_t len = sizeof(error);
	getsockopt(m_sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
	
	int reuse = 1;
	setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
	
	addfd(m_epollfd, this, sockfd, true);

	init();
}

void http_conn::init() 
{
	m_check_state = PARSE_REQUESTLINE_STATE;
	m_linger = false;

	m_content_length = 0;
	m_host = NULL;
    
	m_start_line = 0;
	m_checked_idx = 0;
	m_read_idx = 0;
	m_write_idx = 0;

	m_content_idx = 0;
	m_content_checked_idx = 0;
	end_line = 0;
	is_have_end = false;
	is_file_content = false;
    
    upload_file_id = -1; 
    download_file_id = -1; 
    
	write_count = 0;
	write_file_count = 0;
	
	is_in_epoll = false;
    
    para_map.clear();

	//read_handle = &http_conn::read_request_line_and_headers;
	//write_handle = &http_conn::write_response;
}

LINE_STATUS http_conn::parse_line() 
{
	char temp;
	
	for (; m_checked_idx < m_read_idx; ++m_checked_idx) 
	{
		temp = m_read_buf[m_checked_idx];
		if (temp == '\r') 
		{
			if ((m_checked_idx + 1) == m_read_idx)
			{
				return LINE_OPEN;
			} 
			else if (m_read_buf[m_checked_idx + 1] == '\n')
			{
				m_read_buf[m_checked_idx++] = '\0';
				m_read_buf[m_checked_idx++] = '\0';
				return LINE_OK;
			}

			return LINE_BAD;
		} 
		else if (temp == '\n')
		{
			if ((m_checked_idx > 1)
					&& (m_read_buf[m_checked_idx - 1] == '\r'))
			{
				m_read_buf[m_checked_idx - 1] = '\0';
				m_read_buf[m_checked_idx++] = '\0';
				return LINE_OK;
			}
			
			return LINE_BAD;
			
		}
	}

	return LINE_OPEN;
}

LINE_STATUS http_conn::parse_post_content_header_line() 
{
	char temp;
	for (; m_content_checked_idx < m_content_idx; ++m_content_checked_idx) 
	{
		temp = m_content_buf[m_content_checked_idx];
		if (temp == '\r') 
		{
			if ((m_content_checked_idx + 1) == m_content_idx) 
			{
				return LINE_OPEN;
			} 
			else if (m_content_buf[m_content_checked_idx + 1] == '\n')
			{
				m_content_buf[m_content_checked_idx++] = '\0';
				m_content_buf[m_content_checked_idx++] = '\0';
				return LINE_OK;
			}

			return LINE_BAD;
		} 
		else if (temp == '\n')
		{
			if ((m_content_checked_idx > 1)
					&& (m_read_buf[m_content_checked_idx - 1] == '\r')) 
			{
				m_content_buf[m_content_checked_idx - 1] = '\0';
				m_content_buf[m_content_checked_idx++] = '\0';
				return LINE_OK;
			}
			return LINE_BAD;
		}
	}

	return LINE_OPEN;
}

LINE_STATUS http_conn::get_content_line() 
{
	char temp;

	for (; m_content_checked_idx < m_content_idx; ++m_content_checked_idx) 
	{
		
		temp = m_content_buf[m_content_checked_idx];
		
		if (temp == '\r') 
		{
			if ((m_content_checked_idx + 1) == m_content_idx) 
			{
				this->end_line = m_content_checked_idx + 1;
				
				return LINE_OPEN;
			} 
			else if (m_content_buf[m_content_checked_idx + 1] == '\n') 
			{
				m_content_checked_idx += 2;
				this->end_line = m_content_checked_idx;
				
				return LINE_OK;
			}

			//return LINE_OPEN;
		} 
		else if (temp == '\n') 
		{
			if ((m_content_checked_idx > 1)
					&& (m_content_buf[m_content_checked_idx - 1] == '\r')) 
			{
				m_content_checked_idx += 1;
				this->end_line = m_content_checked_idx;
				return LINE_OK;
			}

			//return LINE_OPEN;
		}
	}
	
	this->end_line = m_content_checked_idx;

	return LINE_OPEN;
}

READ_STATUS http_conn::read_request_line_and_headers() 
{
	//pr_debug("read_request_line_and_headers->sockfd:%d\n", m_sockfd);
	
	if (m_read_idx >= READ_BUFFER_SIZE - 1)
	{
		return BUFF_IS_FULL;
	}

	int bytes_read = 0;
	
	while (true) 
	{
		//pr_debug("read_request_line_and_headers->into while->sockfd:%d\n", m_sockfd);
		
		bytes_read = recv(m_sockfd, m_read_buf + m_read_idx,
				READ_BUFFER_SIZE - m_read_idx, 0);

		//pr_debug("read_request_line_and_headers->bytes_read:%d->sockfd:%d\n", bytes_read, m_sockfd);
		
		if (bytes_read == -1) 
		{
			if ( errno == EAGAIN || errno == EWOULDBLOCK) 
			{
				this->is_read_ready = false;
                
				//pr_debug("read_request_line_and_headers->EAGAIN->sockfd:%d\n", m_sockfd);
                
				return READ_OK;
			}
			
			pr_debug("read_request_line_and_headers->recv(-1)->errno:%d->sockfd:%d\n", errno, m_sockfd);
			
			return READ_ERROR;
		}
		else if (bytes_read == 0)
		{
			pr_debug("read_request_line_and_headers->recv(0)->errno:%d->sockfd:%d\n", errno, m_sockfd);
			
			return PEER_CLOSED;
		}
		
		//statistic data
		thread_arg *thread_arg_pointer = threadpool<abstract_conn>::thread_arg_array + thread_no;		
		if(thread_arg_pointer->is_first)
		{
			thread_arg_pointer->interval_received_bytes_first += bytes_read;
		}
		else
		{
			thread_arg_pointer->interval_received_bytes_second += bytes_read;
		}

		m_read_idx += bytes_read;
		
		if (m_read_idx >= READ_BUFFER_SIZE - 1) 
		{
			
			return BUFF_IS_FULL;
		}
		
	}
	return READ_OK;
}

READ_STATUS http_conn::read_request_content() 
{

	if (m_content_idx >= READ_BUFFER_SIZE - 1) 
	{
		return BUFF_IS_FULL;
	}

	int bytes_read = 0;
	
	while (true)
	{
		bytes_read = recv(m_sockfd, m_content_buf + m_content_idx,
				READ_BUFFER_SIZE - m_content_idx, 0);
				
		if (bytes_read == -1)
		{
			if ( errno == EAGAIN || errno == EWOULDBLOCK) 
			{
				this->is_read_ready = false;
				return READ_OK;
			}
			
			pr_debug("read_request_content->recv(-1)->errno:%d\n", errno);
			
			return READ_ERROR;
			
		} 
		else if (bytes_read == 0) 
		{
			pr_debug("read_request_content->recv(0)->errno:%d\n", errno);
			
			return PEER_CLOSED;
		}

		//statistic data
		thread_arg *thread_arg_pointer = threadpool<abstract_conn>::thread_arg_array + thread_no;		
		if(thread_arg_pointer->is_first)
		{
			thread_arg_pointer->interval_received_bytes_first += bytes_read;
		}
		else
		{
			thread_arg_pointer->interval_received_bytes_second += bytes_read;
		}
		
		m_content_idx += bytes_read;
		
		if (m_content_idx >= READ_BUFFER_SIZE - 1) 
		{
			return BUFF_IS_FULL;
		}
		
	}
	
	return READ_OK;
}

HTTP_CODE http_conn::parse_request_line(char* text)
{
	m_url = strpbrk(text, " \t");
	
	if (!m_url) 
	{
		return BAD_REQUEST;
	}
	
	*m_url++ = '\0';

	//only handle GET and POST request
	char* method = text;
	
	if (strcasecmp(method, "GET") == 0) 
	{
		m_method = GET;
	} 
	else if (strcasecmp(method, "POST") == 0) 
	{
		m_method = POST;
	} 
    else if(strcasecmp(method, "OPTIONS") == 0)
    {
        m_method = OPTIONS;
    }
	else 
	{
		return BAD_REQUEST;
	}

	m_url += strspn(m_url, " \t");
	m_version = strpbrk(m_url, " \t");
	
	if (!m_version) 
	{
		return BAD_REQUEST;
	}
	
	*m_version++ = '\0';
	m_version += strspn(m_version, " \t");
	
	/*if (strcasecmp(m_version, "HTTP/1.1") != 0 && strcasecmp(m_version, "HTTP/1.0") != 0) 
	{
		return BAD_REQUEST;
	}

	if (strncasecmp(m_url, "http://", 7) == 0) 
	{
		m_url += 7;
		m_url = strchr(m_url, '/');
	}

	if (!m_url || m_url[0] != '/') 
	{
		return BAD_REQUEST;
	}*/
    
    if(!parse_url_2(m_url, m_url, para_map))
    {
        return BAD_REQUEST;
    }

	return GO_TO_NEXT;
}

HTTP_CODE http_conn::parse_headers(char* text)
{
	
	if (text[0] == '\0') 
	{

		if (m_content_length != 0) 
		{

			return GO_TO_NEXT;
		}

		return HANDLE_REQUEST;

	} 
	else if (strncasecmp(text, "Connection:", 11) == 0) 
	{

		text += 11;
		text += strspn(text, " \t");
		if (strcasecmp(text, "keep-alive") == 0)
        {
			//m_linger = true;
		}

	} 
	else if (strncasecmp(text, "Content-Length:", 15) == 0) 
	{

		text += 15;
		text += strspn(text, " \t");
		m_content_length = atol(text);

	} 
	else if (strncasecmp(text, "Host:", 5) == 0) 
	{

		text += 5;
		text += strspn(text, " \t");
		m_host = text;

	} 
    else if (strncasecmp(text, "Referer:", 8) == 0) 
	{

		text += 8;
		text += strspn(text, " \t");
		m_referer = text;
        if (strncasecmp(m_referer, "http://", 7) == 0) 
        {
            m_referer += 7;
        }

	}
	else if (strncasecmp(text, "Content-Type:", 13) == 0) 
	{

		text += 13;
		text += strspn(text, " \t");
		m_content_type = text;

		if ((text = strstr(m_content_type, "boundary=")) != NULL) 
		{
			text = text + 9;
			boundary = text;
			boundary_length = strlen(boundary);
			end_boundary_length = boundary_length + 2;
		}
		
		//pr_debug("content_type:%s\tboundary:%s\n", m_content_type, boundary);

	} else {

		//pr_debug("oop! unknow header %s\n", text);

	}

	return UNCOMPLETE_REQUEST;
}

HTTP_CODE http_conn::parse_post_content_headers(char* text) 
{
	
	if (text[0] == '\0')
	{

		return GO_TO_NEXT;

	} 
	else if (strncasecmp(text, "Content-Disposition:", 20) == 0)
	{
		text += 20;
		text += strspn(text, " \t");
		
		char *filename_index = strstr(text, "filename=");
		
		if (filename_index != NULL)
		{
			filename_index = filename_index + 9 + 1;
			filename_index[strlen(filename_index) - 1] = '\0';
			strncpy(m_upload_file_name, filename_index, FILENAME_LEN);
			is_file_content = true;
            
            //if it is upload request then rename file name
            if(strcmp(m_url, UPLOAD_STRING) == 0)
            {
                get_file_name(m_upload_file_name, m_upload_file_name);
            }
			
			//pr_debug("filename:%s\n", m_upload_file_name);
		} 
		else 
		{
			filename_index = strstr(text, "name=");
			
			//pr_debug("name:%s\n", filename_index);
		}

	} 
	else 
	{
		//pr_debug("oop! unknow header %s\n", text);
	}

	return UNCOMPLETE_REQUEST;
}

HTTP_CODE http_conn::parse_content(char* text) 
{
	if (m_read_idx >= (m_content_length + m_checked_idx)) 
	{
		text[m_content_length] = '\0';
		return HANDLE_REQUEST;
	}

	return UNCOMPLETE_REQUEST;
}

HTTP_CODE http_conn::parse_post_content() 
{
	LINE_STATUS line_status = LINE_OK;
	int line_length = 0;
	
	while (true) 
	{
		line_status = get_content_line();
		line_length = this->end_line;
		
		//pr_debug("m_content_buf:%d\n", line_length);
		
		if (line_status == LINE_OK) 
		{
			//pr_debug("LINE_OK\n");
			
			if (line_length == (boundary_length + 4)
					|| line_length == (end_boundary_length + 4)) 
			{
				if (memcmp(boundary, m_content_buf + 2, boundary_length) == 0) 
				{
					if (upload_file_id > 0) 
					{
						close(upload_file_id);
						is_file_content = false;
                        upload_file_id = -1;
					}

					if (line_length == (end_boundary_length + 4)) 
					{						
						return HANDLE_REQUEST;
					} 
					else 
					{
						return GO_TO_NEXT;
					}
				}
			}

			int writecount = 0;

			if (is_file_content) 
			{
				if (is_have_end) 
				{
					const char *crlf = "\r\n";
                    
                    write(upload_file_id,crlf, 2);
                    
                    /*
					while ((writecount = write(upload_file_id,
							crlf + writecount, 2 - writecount)) > 0);
                    */
                    
					writecount = 0;
				}
                
                write(upload_file_id, m_content_buf, line_length - 2);

				/*
                 while ((writecount = write(upload_file_id,
						m_content_buf + writecount,
						line_length - 2 - writecount)) > 0);
                */
			}

			char *src = m_content_buf + line_length;
			m_content_idx -= line_length;
			memmove(m_content_buf, src, m_content_idx);
            
			m_content_checked_idx = 0;

			is_have_end = true;

		} 
		else
		{
			//pr_debug("LINE_Bad\n");
			
			int writecount = 0;

			if (is_file_content) 
			{
				if (line_length - 2 > boundary_length) 
				{
                    if (is_have_end)
                    {
                        const char *crlf = "\r\n";
                        
                        write(upload_file_id, crlf, 2);
                        
                        /*
                        while ((writecount = write(upload_file_id,
                                crlf + writecount, 2 - writecount)) > 0);
                        */
                        
                        writecount = 0;
                        
                        is_have_end = false;
                    }
                    
                    write(upload_file_id, m_content_buf, line_length);
                    
                    /*
					while ((writecount = write(upload_file_id,
							m_content_buf + writecount,
							line_length - writecount)) > 0);
                    */
                    
					m_content_checked_idx = 0;
					m_content_idx = 0;
				}
			}
			
			return UNCOMPLETE_REQUEST;
		}
	}
}

/*
 * 
 * name: http_conn::judge_request_type
 * @param
 * @return
 * 
 */
HTTP_CODE http_conn::judge_request_type() 
{
	
	//upload file request
    if (m_method == OPTIONS) 
	{
		//pr_debug("UPLOAD_FILE_REQUEST\n");
        
		return OPTIONS_REQUEST;
	}
    
	if (strcmp(m_url, UPLOAD_STRING) == 0) 
	{
		//pr_debug("UPLOAD_FILE_REQUEST\n");
        
		return UPLOAD_FILE_REQUEST;
	}
    
    if (strcmp(m_url, FORWARD_STRING) == 0) 
	{
		pr_debug("FORWARD_FILE_REQUEST\n");
        
		return FORWARD_FILE_REQUEST;
	}
    
    if (strcmp(m_url, DOWNLOAD_STRING) == 0) 
	{
		pr_debug("FILE_REQUEST\n");
        
        char *file_name_key = "realname";
        char *client_name_key = "clientname";
        
        std::map<char*, char*, classcomp>::iterator para_map_iter = para_map.find(file_name_key);
        
        if(para_map_iter == para_map.end())
        {
            pr_debug("NO_RESOURCE\n");
            return NO_RESOURCE;
        }
        
        //bug : buff overfit
        strcpy(m_real_file, doc_root);
        strcat(m_real_file, "/");
        strcat(m_real_file, para_map_iter->second);
        
        char *real_file_name = para_map_iter->second;
        
        para_map_iter = para_map.find(client_name_key);
        if(para_map_iter == para_map.end())
        {
            strcpy(m_upload_file_name, real_file_name);
        }
        else
        {
            pr_debug("clientname:%s\n", para_map_iter->second);
            strcpy(m_upload_file_name, para_map_iter->second);
            pr_debug("m_upload_file_name:%s\n", m_upload_file_name);
        }
        
        pr_debug("m_real_file:%s\n", m_real_file);
        
        if (stat(m_real_file, &m_file_stat) < 0)
        {
            pr_debug("NO_RESOURCE\n");
            return NO_RESOURCE;
        }
        
        download_file_id = open(m_real_file, O_RDONLY);
        
        //simple judge 
        if(download_file_id < 0)
        {
            pr_debug("INTERNAL_ERROR\n");
            return INTERNAL_ERROR;
        }
        
        /*	
        m_file_address = (char*) mmap(0, m_file_stat.st_size, PROT_READ,
        MAP_PRIVATE, download_file_id, 0);
        
        if(m_file_address == NULL)
        {
            close(download_file_id);
            return INTERNAL_ERROR;
        }
        
        close(download_file_id);
        */
        
        return FILE_REQUEST;
	}

	strcpy(m_real_file, doc_root);
    int len = strlen(doc_root);
    strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);
    
    strcpy(m_upload_file_name, m_url + 1);
    
    if (stat(m_real_file, &m_file_stat) < 0)
    {
        return NO_RESOURCE;
    }

	if (!(m_file_stat.st_mode & S_IROTH)) 
	{
		//pr_debug("FORBIDDEN_REQUEST\n");
        
		return FORBIDDEN_REQUEST;
	}

	if (S_ISDIR(m_file_stat.st_mode)) 
	{
		//pr_debug("LIST_DIR_REQUEST\n");
        
		return LIST_DIR_REQUEST;
	}
    
    download_file_id = open(m_real_file, O_RDONLY);
        
    //simple judge 
    if(download_file_id < 0)
    {
        pr_debug("INTERNAL_ERROR\n");
        return INTERNAL_ERROR;
    }
    
    return FILE_REQUEST;
}

void http_conn::unmap() 
{
	
	if (m_file_address) 
	{
		munmap(m_file_address, m_file_stat.st_size);
		m_file_address = NULL;
	}
    
    if (download_file_id > 0) 
	{
		close(download_file_id);
		download_file_id = -1;
	}
}

bool http_conn::write_response() 
{
    //pr_debug("write_buf:%s\n", m_write_buf);
    int state = 1;
    setsockopt(m_sockfd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
    
	int temp = 0;
	
	while (true)
	{
		if (write_count >= m_write_idx) 
		{
			break;
		}
		
		temp = write(m_sockfd, m_write_buf + write_count,
				m_write_idx - write_count);
        
		if (temp <= -1)
		{
			
			if ( errno == EAGAIN || errno == EWOULDBLOCK)
			{
				is_in_epoll = true;
				modfd(m_epollfd, m_sockfd, EPOLLOUT);
				return true;
			}
			
			pr_debug("write response header error->sockfd:%d->errno:%d\n",m_sockfd, errno);
			
            unmap();
			close_conn();
			return false;
		}

		write_count += temp;
		
		if (write_count >= m_write_idx) 
		{
			//pr_debug("write response->sockfd:%d\n", m_sockfd);
			
			break;
		}
	}

	//if (m_file_address != NULL)
    if(download_file_id > 0)
	{
        
        //int state = 1;
        //setsockopt(m_sockfd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
		//pr_debug("start write file\n");
		
		while (true) 
		{
			if (write_file_count >= download_file_size)
			{
				unmap();
				break;
			}
			
            temp = sendfile(m_sockfd, download_file_id, &write_file_count, download_file_size - write_file_count);
            
            //pr_debug("temp:%d->write_file_count:%ld\n", temp, write_file_count);
            
			//temp = write(m_sockfd, m_file_address + write_file_count,download_file_size - write_file_count);
					
			if (temp <= -1) 
			{
				if ( errno == EAGAIN || errno == EWOULDBLOCK) 
				{
					modfd(m_epollfd, this, m_sockfd, EPOLLOUT, is_in_epoll);
                    is_in_epoll = true;
					return true;
				}
				
				//pr_debug("write file error->sockfd:%d->errno:%d\n", m_sockfd, errno);
				
				unmap();
				close_conn();
				return false;
			}

			//write_file_count += temp;
			
			if (write_file_count >= download_file_size) 
			{
				unmap();
				break;
			}
		}
        
        //state = 0;
        //setsockopt(m_sockfd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
	}
    
    state = 0;
    setsockopt(m_sockfd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));
    
    //pr_debug("response done close_conn->sockfd:%d->errno:%d\n", m_sockfd, errno);
	close_conn();
	return false;
}

bool http_conn::add_response(const char* format, ...) 
{
	if (m_write_idx >= WRITE_BUFFER_SIZE)
	{
		return false;
	}
	
	va_list arg_list;
	va_start(arg_list, format);
	
	int len = vsnprintf(m_write_buf + m_write_idx,
			WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list);
			
	if (len >= (WRITE_BUFFER_SIZE - 1 - m_write_idx)) 
	{
		return false;
	}
	
	m_write_idx += len;
	
	va_end(arg_list);
	
	return true;
}

bool http_conn::add_status_line(int status, const char* title)
{
	return add_response("%s %d %s\r\n", "HTTP/1.1", status, title);
}

bool http_conn::add_headers(int content_len) 
{
	add_content_length(content_len);
	add_linger();
	add_blank_line();
}

bool http_conn::add_content_length(int content_len) 
{
	return add_response("Content-Length: %d\r\n", content_len);
}

bool http_conn::add_linger() 
{
	return add_response("Connection: %s\r\n",
			(m_linger == true) ? "keep-alive" : "close");
}

bool http_conn::add_blank_line() 
{
	return add_response("%s", "\r\n");
}

bool http_conn::add_content(const char* content) 
{
	return add_response("%s", content);
}

//generate response
bool http_conn::generate_response(HTTP_CODE ret) 
{
    abstract_conn *abstract_conn_ptr = NULL;
    char content_string[256] = "";
    int len = 0;
    
	switch (ret)
    {
        case INTERNAL_ERROR: 
        
            add_status_line(500, error_500_title);
            add_headers(strlen(error_500_form));
            if (!add_content(error_500_form))
            {
                return false;
            }
            break;
            
        case OPTIONS_REQUEST: 
        
            add_status_line(200, ok_200_title);
            add_response("%s","Access-Control-Allow-Origin:*\r\n");
            add_response("%s","Access-Control-Allow-Methods:GET,POST\r\n");
            add_headers(0);
            
            break;

        case LIST_DIR_REQUEST: 
        
            add_status_line(200, ok_200_title);
            list_dir(m_real_file);
            add_headers(strlen(list_dir_content));
            if (!add_content(list_dir_content))
            {
                return false;
            }

            break;

        case FILE_EXIST_REQUEST: 
        
            add_status_line(200, ok_200_title);
            add_headers(strlen(ok_200_file_exist_string));
            if (!add_content(ok_200_file_exist_string))
            {
                return false;
            }
            break;

        case UPLOAD_FILE_REQUEST: 
        
            if(is_use_forward)
            {
                abstract_conn_ptr = new forward_conn;
                
                strcpy(abstract_conn_ptr->m_upload_file_name, this->m_upload_file_name);
                
                if(abstract_conn_ptr->init(REMOTE_PORT, REMOTE_IP))
                {
                    addfd_EPOLLOUT(m_epollfd, abstract_conn_ptr, abstract_conn_ptr->m_sockfd, true);
                }
                else
                {
                    pr_debug("forward connect error\n");
                    delete abstract_conn_ptr;
                }
            }
            
            add_status_line(302, "Found");
            stat(m_real_file, &m_file_stat);
            strcat(content_string, "1+");
            strcat(content_string, m_upload_file_name);
            strcat(content_string, "+");
            len = strlen(content_string);
            sprintf(content_string+len, "%ld", m_file_stat.st_size);
            
            add_response("Location:%s?msg=%s\r\n%s",REDIRECT_HOST, content_string);
            //add_response("%s","Access-Control-Allow-Origin:*\r\n");
           //add_response("%s","Access-Control-Allow-Methods:GET,POST\r\n");
            //add_headers(strlen(content_string));
            add_headers(0);
            
            /*
            if (!add_content(content_string)) 
            {
                return false;
            }
            */

            break;
            
        case FORWARD_FILE_REQUEST: 
        
            add_status_line(200, ok_200_title);
            add_headers(strlen(ok_200_string));
            if (!add_content(ok_200_string))
            {
                return false;
            }

            break;

        case BAD_REQUEST: 
        
            add_status_line(400, error_400_title);
            add_headers(strlen(error_400_form));
            
            if (!add_content(error_400_form))
            {
                return false;
            }
            
            break;

        case NO_RESOURCE: 
        
            add_status_line(404, error_404_title);
            add_headers(strlen(error_404_form));
            
            if (!add_content(error_404_form))
            {
                return false;
            }
            
            break;

        case FORBIDDEN_REQUEST:
         
            add_status_line(403, error_403_title);
            add_headers(strlen(error_403_form));
            
            if (!add_content(error_403_form))
            {
                return false;
            }
            
            break;

        case FILE_REQUEST:
        
            add_status_line(200, ok_200_title);
            
            if (m_file_stat.st_size != 0) 
            {
                strcat(content_string, "Content-disposition: attachment;filename=");
                strcat(content_string, m_upload_file_name);
                strcat(content_string, "\r\n");
                
                pr_debug("content_string:%s\n", content_string);
                
                add_response("%s", content_string);
                
                add_headers(m_file_stat.st_size);
                download_file_size = m_file_stat.st_size;
                
                return true;
            } 
            else 
            {
                const char* ok_string = "<html><body>file is empty!</body></html>";
                add_response("%s", "Content-type:text/html\r\n");
                add_headers(strlen(ok_string));
                
                if (!add_content(ok_string))
                {
                    return false;
                }
                
            }
            
            break;

        default: 
            return false;

	}

	return true;
}

READ_STATUS http_conn::read_request() 
{    
	if (m_check_state == PARSE_POST_CONTENT_STATE
			|| m_check_state == PARSE_POST_CONTENT_HEADER_STATE) 
	{
		return read_request_content();
	} 
	else 
	{
		return read_request_line_and_headers();
	}
}

HTTP_CODE http_conn::parse_request_line() 
{	
	LINE_STATUS line_status = LINE_OK;
	HTTP_CODE ret = UNCOMPLETE_REQUEST;
	char* text = 0;

	line_status = parse_line();
	
	if (line_status == LINE_OK) 
	{
		text = get_line();
		m_start_line = m_checked_idx;
		return parse_request_line(text);
	} 
	else if (line_status == LINE_BAD)
	{
		return BAD_REQUEST;
	}
	
	return UNCOMPLETE_REQUEST;
}

HTTP_CODE http_conn::parse_post_content_headers() 
{
	LINE_STATUS line_status = LINE_OK;
	HTTP_CODE ret = UNCOMPLETE_REQUEST;
	char* text = 0;

	while (true) 
	{
		line_status = parse_post_content_header_line();

		//printf("parse line text:%s\n", m_content_buf);

		switch (line_status) 
		{
            case LINE_OK:
                text = get_content_hearder_line();
                
                //m_start_line = m_content_checked_idx;
                
                ret = parse_post_content_headers(text);
                
                if (m_content_checked_idx != 0) 
                {
                    m_content_idx -= m_content_checked_idx;
                    memmove(m_content_buf, m_content_buf + m_content_checked_idx,
                            m_content_idx);

                    //m_content_buf[m_content_idx] = '\0';
                    
                    m_content_checked_idx = 0;
                }
                
                if (ret == GO_TO_NEXT) 
                {
                    return GO_TO_NEXT;
                } 
                else if (ret == BAD_REQUEST) 
                {
                    return BAD_REQUEST;
                }
                break;
                
            case LINE_BAD:
                return BAD_REQUEST;
                
            default:
                return UNCOMPLETE_REQUEST;
		}
	}
}

HTTP_CODE http_conn::parse_headers() 
{
	//pr_debug("parse_headers->sockfd:%d\n", m_sockfd);
    
	LINE_STATUS line_status = LINE_OK;
	HTTP_CODE ret = UNCOMPLETE_REQUEST;
	char* text = 0;

	while (true) 
	{
		line_status = parse_line();
		
		switch (line_status) 
		{
				case LINE_OK:
					text = get_line();
					m_start_line = m_checked_idx;
					
					ret = parse_headers(text);
                    
					//pr_debug("%d:ret\n", ret);
                    
					if (ret == GO_TO_NEXT) 
					{
						return GO_TO_NEXT;
					}
					else if (ret == BAD_REQUEST) 
					{
						pr_debug("parse header error->sockfd:%d->errno\n", m_sockfd, errno);
                        
						return BAD_REQUEST;
					} 
					else if (ret == HANDLE_REQUEST)
					{
						return HANDLE_REQUEST;
					}
					break;
					
				case LINE_BAD:
					pr_debug("parse header error->sockfd:%d->errno\n", m_sockfd, errno);
                    
					return BAD_REQUEST;
					
				default:
					return UNCOMPLETE_REQUEST;
		}
	}
}

HTTP_CODE http_conn::parse_request() 
{
	//pr_debug("parse_request->sockfd:%d\n",m_sockfd);
	
	HTTP_CODE http_code;
	
	switch (m_check_state) 
	{
		case PARSE_REQUESTLINE_STATE:
		
			//pr_debug("parse request line\n");
			
			http_code = parse_request_line();
			
			if (http_code == BAD_REQUEST) 
			{
				pr_debug("parse request line error->sockfd:%d->errno:%d\n", m_sockfd, errno);
				
				return BAD_REQUEST;
			} 
			else if (http_code == UNCOMPLETE_REQUEST) 
			{
				return UNCOMPLETE_REQUEST;
			} 
			else if (http_code == GO_TO_NEXT) 
			{
				m_check_state = PARSE_HEADER_STATE;
			} 
			else 
			{
				return http_code;
			}
			
		case PARSE_HEADER_STATE:
		
			//pr_debug("parse request header\n");
			
			http_code = parse_headers();
			
			if (http_code == BAD_REQUEST) 
			{
				pr_debug("parse headers error->sockfd:%d->errno:%d\n", m_sockfd, errno);
				
				return BAD_REQUEST;
			} 
			else if (http_code == UNCOMPLETE_REQUEST)
			{
				return UNCOMPLETE_REQUEST;
			} 
			else if (http_code == GO_TO_NEXT)
			{
				if (m_method == POST) 
				{
					m_check_state = PARSE_POST_CONTENT_HEADER_STATE;
					m_start_line = 0;

					m_content_idx = m_read_idx - m_checked_idx;
					m_content_checked_idx = 0;

					memmove(m_content_buf, m_read_buf + m_checked_idx,
							m_content_idx);
                            
					//pr_debug("length of read buf: %d\n", m_read_idx);
					//pr_debug("length of content buf: %d\n",m_read_idx - m_checked_idx);

				} 
				else 
				{
					m_check_state = PARSE_CONTENT_STATE;
                    
					//strcpy(m_read_buf+m_checked_idx,m_content_buf);
				}
				
			} 
			else 
			{
				return http_code;
			}
			
		case PARSE_POST_CONTENT_HEADER_STATE:

			parse_content_header:

			//pr_debug("parse_content_header\n");

			http_code = parse_post_content_headers();
			
			if (http_code == BAD_REQUEST) 
			{
				
				pr_debug("parse_content_header error\n");
				
				return BAD_REQUEST;
			} 
			else if (http_code == UNCOMPLETE_REQUEST) 
			{
				return UNCOMPLETE_REQUEST;
			} 
			else if (http_code == GO_TO_NEXT) 
			{
				m_check_state = PARSE_POST_CONTENT_STATE;
				
				if (is_file_content) 
				{
                    m_real_file[0] = '\0';
					strcat(m_real_file, doc_root);
					strcat(m_real_file, "/");
					strcat(m_real_file, m_upload_file_name);
                    
                    if (stat(m_real_file, &m_file_stat) >= 0)
                    {
                        //bug:lead to reset connection
                        return FILE_EXIST_REQUEST;
                    }
                    
                    upload_file_id = open(m_real_file, O_RDWR | O_CREAT, 0666);
                    
				}
				
				m_content_idx -= m_content_checked_idx;
				memmove(m_content_buf, m_content_buf + m_content_checked_idx,
						m_content_idx);
				m_content_checked_idx = 0;
				
			} 
			else 
			{
				return http_code;
			}

		case PARSE_POST_CONTENT_STATE:
		
			//pr_debug("PARSE_POST_CONTENT_STATE\n");
			
			http_code = parse_post_content();
			
			if (http_code == BAD_REQUEST) 
			{
				return BAD_REQUEST;
			} 
			else if (http_code == UNCOMPLETE_REQUEST) 
			{
				return UNCOMPLETE_REQUEST;
			} 
			else if (http_code == GO_TO_NEXT) 
			{
				m_check_state = PARSE_POST_CONTENT_HEADER_STATE;

				//pr_debug("java:%s\n", m_content_buf);
				
				m_content_idx -= m_content_checked_idx;
				memmove(m_content_buf, m_content_buf + m_content_checked_idx,
						m_content_idx);
				m_content_checked_idx = 0;
				
				//pr_debug("m_content_idx:%d\n", m_content_idx);

				goto parse_content_header;

			} 
			else
			{
				return http_code;
			}

		default:
			return INTERNAL_ERROR;
	}
	
}

HTTP_CODE http_conn::read_handle_func() 
{
	READ_STATUS read_status;
	
	while (this->is_read_ready)
	{
		//pr_debug("is_read_ready\n");
		
		read_status = read_request();
		
		//pr_debug("read_request return->sockfd:%d\n", m_sockfd);
		
		switch (read_status) 
		{
			case READ_OK:
			case BUFF_IS_FULL:
				switch (parse_request()) 
				{
					case HANDLE_REQUEST:
                    
						generate_response(judge_request_type());
						//modfd(m_epollfd, this, m_sockfd, EPOLLOUT, is_in_epoll);
						//is_in_epoll = true;
                        this->handle_type = WRITE;
                        this->process();
						return HANDLE_REQUEST;
						
					case BAD_REQUEST:
                    
						generate_response(BAD_REQUEST);
						
						//modfd(m_epollfd, this, m_sockfd, EPOLLOUT, is_in_epoll);
						//is_in_epoll = true;
						this->handle_type = WRITE;
                        this->process();
						return BAD_REQUEST;
						
					case FILE_EXIST_REQUEST:
                    
						generate_response(FILE_EXIST_REQUEST);
						
						//modfd(m_epollfd, this, m_sockfd, EPOLLOUT, is_in_epoll);
						//is_in_epoll = true;
						this->handle_type = WRITE;
                        this->process();
						return FILE_EXIST_REQUEST;
						
					case UNCOMPLETE_REQUEST:
                    
						if (read_status != BUFF_IS_FULL) 
						{
							modfd(m_epollfd, this, m_sockfd, EPOLLIN, is_in_epoll);
							is_in_epoll = true;
							
							return UNCOMPLETE_REQUEST;
						}
						
						break;
				 }
					
                break;

            case PEER_CLOSED:

            case READ_ERROR:
                
                pr_debug("read_handle_func->READ_ERROR->sockfd:%d\n",m_sockfd);
					
                close_conn();
					
                return ERROR_REQUEST;
		}
	}

	if (!is_read_ready) 
	{
		modfd(m_epollfd, this, m_sockfd, EPOLLIN, is_in_epoll);
		is_in_epoll = true;
		return UNCOMPLETE_REQUEST;
	}
}

HTTP_CODE http_conn::write_handle_func() 
{
	write_response();
	return RESPONSE_ERROR;
}

void http_conn::process() 
{
	//pr_debug("Process\n");
    
	switch (handle_type) 
    {
        case READ:
            read_handle_func();
            break;
            
        case WRITE:
            write_handle_func();
            break;

        default:
            //do nothing
            break;
	}
}

char * http_conn::list_dir(char *path)
{
	memset(list_dir_content, '\0', LISTDIRCONTENT_LEN);
	DIR *dp;
	struct dirent *dirp;
	if ((dp = opendir(path)) == NULL) {
		pr_debug("can't open %s", path);
		return list_dir_content;
	}

	strcat(list_dir_content, list_dir_start);

	while ((dirp = readdir(dp)) != NULL) {
		if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) {
			continue;
		}
		strcat(list_dir_content, "<a href=\"/download?realname=");
		strcat(list_dir_content, dirp->d_name);
		strcat(list_dir_content, "\">");
		strcat(list_dir_content, dirp->d_name);
		strcat(list_dir_content, "</a><br />");
	}

	strcat(list_dir_content, list_dir_end);

	return list_dir_content;
}

